package heap

import "go_gvm_mini/classfile"

type InterfaceMethodRef struct {
	MethodRef
	method *Method
}

func newInterfaceMethodRef(cp *ConstantPool, refInfo *classfile.Constant_Interface_Method_Ref) *InterfaceMethodRef {
	ref := &InterfaceMethodRef{}
	ref.constantPool = cp
	ref.copyMemberRefInfo(refInfo)
	return ref
}

func (imr *InterfaceMethodRef) ResolvedInterfaceMethod() *Method {
	if imr.method == nil {
		imr.resolveInterfaceMethodRef()
	}
	return imr.method
}

func (imr *InterfaceMethodRef) resolveInterfaceMethodRef() {
	d := imr.constantPool.class
	c := imr.ResolvedClass()
	if !c.IsInterface() {
		panic("java.lang.IncompatibleClassChangeError")
	}
	method := lookupInterfaceMethod(c, imr.name, imr.descriptor)
	if method == nil {
		panic("java.lang.NoSuchMethodError")
	}
	if !method.isAccessibleTo(d) {
		panic("java.lang.IllegalAccessError")
	}
	imr.method = method
}

func (mr *InterfaceMethodRef) copyMemberRefInfo(refInfo *classfile.Constant_Interface_Method_Ref) {
	mr.className = refInfo.ClassName()
	mr.name, mr.descriptor = refInfo.NameAndDescriptor()
}

func lookupInterfaceMethod(iface *Klass, name string, descriptor string) *Method {
	for _, method := range iface.methods {
		if method.name == name && method.descriptor == descriptor {
			return method
		}
	}
	return lookupMethodInInterfaces(iface.interfaces, name, descriptor)
}

func lookupMethodInInterfaces(interfaces []*Klass, name string, descriptor string) *Method {
	for _, iface := range interfaces {
		for _, method := range iface.methods {
			if method.name == name && method.descriptor == descriptor {
				return method
			}
		}
		method := lookupMethodInInterfaces(iface.interfaces, name, descriptor)
		if method != nil {
			return method
		}
	}
	return nil
}
